/*
*  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
*  Use of this source code is governed by a BSD-style license
*  that can be found in the LICENSE file in the root of the source
*  tree. An additional intellectual property rights grant can be found
*  in the file PATENTS.  All contributing project authors may
*  be found in the AUTHORS file in the root of the source tree.
*/

#ifndef SEQUENCE_NUMBER_UTIL_H_
#define SEQUENCE_NUMBER_UTIL_H_

#include <stdint.h>
#ifdef max
#undef max
#endif
#include <limits>
#include <type_traits>


namespace jx {

#define MaxUint16 65535
	// Calculates the forward difference between two wrapping numbers.
	//
	// Example:
	// uint8_t x = 253;
	// uint8_t y = 2;
	//
	// ForwardDiff(x, y) == 5
	//
	//   252   253   254   255    0     1     2     3
	// #################################################
	// |     |  x  |     |     |     |     |  y  |     |
	// #################################################
	//          |----->----->----->----->----->
	//
	// ForwardDiff(y, x) == 251
	//
	//   252   253   254   255    0     1     2     3
	// #################################################
	// |     |  x  |     |     |     |     |  y  |     |
	// #################################################
	// -->----->                              |----->---
	//
	// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the
	// largest value representable by T.
	template <typename T, T M>
	inline typename std::enable_if<(M > 0), T>::type ForwardDiff(T a, T b) {
		static_assert(std::is_unsigned<T>::value,
			"Type must be an unsigned integer.");
		//RTC_DCHECK_LT(a, M);
		//RTC_DCHECK_LT(b, M);
		if (a >= M) { exit(-1); }
		if (b >= M) { exit(-1); }
		return a <= b ? b - a : M - (a - b);
	}

	template <typename T, T M>
	inline typename std::enable_if<(M == 0), T>::type ForwardDiff(T a, T b) {
		static_assert(std::is_unsigned<T>::value,
			"Type must be an unsigned integer.");
		return b - a;
	}

	template <typename T>
	inline T ForwardDiff(T a, T b) {
		return ForwardDiff<T, 0>(a, b);
	}

	// Calculates the reverse difference between two wrapping numbers.
	//
	// Example:
	// uint8_t x = 253;
	// uint8_t y = 2;
	//
	// ReverseDiff(y, x) == 5
	//
	//   252   253   254   255    0     1     2     3
	// #################################################
	// |     |  x  |     |     |     |     |  y  |     |
	// #################################################
	//          <-----<-----<-----<-----<-----|
	//
	// ReverseDiff(x, y) == 251
	//
	//   252   253   254   255    0     1     2     3
	// #################################################
	// |     |  x  |     |     |     |     |  y  |     |
	// #################################################
	// ---<-----|                             |<-----<--
	//
	// If M > 0 then wrapping occurs at M, if M == 0 then wrapping occurs at the
	// largest value representable by T.
	template <typename T, T M>
	inline typename std::enable_if<(M > 0), T>::type ReverseDiff(T a, T b) {
		static_assert(std::is_unsigned<T>::value,
			"Type must be an unsigned integer.");
		//RTC_DCHECK_LT(a, M);
		//RTC_DCHECK_LT(b, M);

		if (a >= M) { exit(-1); }
		if (b >= M) { exit(-1); }
		return b <= a ? a - b : M - (b - a);
	}

	template <typename T, T M>
	inline typename std::enable_if<(M == 0), T>::type ReverseDiff(T a, T b) {
		static_assert(std::is_unsigned<T>::value,
			"Type must be an unsigned integer.");
		return a - b;
	}

	template <typename T>
	inline T ReverseDiff(T a, T b) {
		return ReverseDiff<T, 0>(a, b);
	}

	// Calculates the minimum distance between to wrapping numbers.
	//
	// The minimum distance is defined as min(ForwardDiff(a, b), ReverseDiff(a, b))
	template <typename T, T M = 0>
	inline T MinDiff(T a, T b) {
		static_assert(std::is_unsigned<T>::value,
			"Type must be an unsigned integer.");
		return std::min(ForwardDiff<T, M>(a, b), ReverseDiff<T, M>(a, b));
	}


	// Test if the sequence number |a| is ahead or at sequence number |b|.
	//
	// If |M| is an even number and the two sequence numbers are at max distance
	// from each other, then the sequence number with the highest value is
	// considered to be ahead.
	template <typename T, T M>
	inline typename std::enable_if<(M > 0), bool>::type AheadOrAt(T a, T b) {
		static_assert(std::is_unsigned<T>::value,
			"Type must be an unsigned integer.");
		const T maxDist = M / 2;
		if (!(M & 1) && MinDiff<T, M>(a, b) == maxDist)
			return b < a;
		return ForwardDiff<T, M>(b, a) <= maxDist;
	}

	template <typename T, T M>
	inline typename std::enable_if<(M == 0), bool>::type AheadOrAt(T a, T b) {
		static_assert(std::is_unsigned<T>::value,
			"Type must be an unsigned integer.");
		const T maxDist = std::numeric_limits<T>::max() / 2 + T(1);
		//const T maxDist = MaxUint16 / 2 + T(1);
		if (a - b == maxDist)
			return b < a;
		return ForwardDiff(b, a) < maxDist;
	}

	template <typename T>
	inline bool AheadOrAt(T a, T b) {
		return AheadOrAt<T, 0>(a, b);
	}

	// Test if the sequence number |a| is ahead of sequence number |b|.
	//
	// If |M| is an even number and the two sequence numbers are at max distance
	// from each other, then the sequence number with the highest value is
	// considered to be ahead.
	template <typename T, T M = 0>
	inline bool AheadOf(T a, T b) {
		static_assert(std::is_unsigned<T>::value,
			"Type must be an unsigned integer.");
		return a != b && AheadOrAt<T, M>(a, b);
	}


	// A sequence number unwrapper where the first unwrapped value equals the
	// first value being unwrapped.
	template <typename T, T M = 0>
	class SeqNumUnwrapper {

#if 1
		static_assert(
			std::is_unsigned<T>::value &&
			std::numeric_limits<T>::max() < std::numeric_limits<int64_t>::max(),
			"Type unwrapped must be an unsigned integer smaller than int64_t.");
#endif

	public:
		int64_t Unwrap(T value) {
			if (!b_isInit) {
				last_unwrapped_ = value;
				b_isInit = true;
			}
			else {
				last_unwrapped_ += ForwardDiff<T, M>(last_value_, value);

				if (!AheadOrAt<T, M>(value, last_value_)) {
					//int64_t TMax = MaxUint16; 
					int64_t TMax = std::numeric_limits<T>::max();
					/*constexpr*/ int64_t kBackwardAdjustment =
						M == 0 ? int64_t{ TMax } +1 : M;
					last_unwrapped_ -= kBackwardAdjustment;
				}
			}

			last_value_ = value;
			return last_unwrapped_;
		}

	private:
		int64_t last_unwrapped_ = 0;
		T last_value_;
		bool b_isInit = false;
	};

}  // namespace webrtc

#endif  // RTC_BASE_NUMERICS_SEQUENCE_NUMBER_UTIL_H_
